/**
 * 统计文件内容行数
 *
 * @author lengduan
 * @date 2020.8.1
 */
use std::env::args;
use std::fs;
use std::str::FromStr;
use std::time;

/// 默认配置
///
/// is_show_file: 是否打印所有文件路径及行数，默认 false
///
/// is_delete_empty_line: 是否删除空行，默认 true
///
/// sort_flag: 打印的文件路径的顺序，根据文件行数排序，共有升序-1、降序--1，原序-0（即读入顺序）三种显示方式， 默认 1 升序
///
/// gte: 文件行数至少多少行才打印，默认为0（不判断）
///
/// lte: 文件行数不超过多少行才打印，默认为0（不判断）
///
/// paths: 存储要读取的路径，处理输入多个路径的情况
struct Options {
    is_show_file: bool,
    is_delete_empty_line: bool,
    sort_flag: i8,
    gte: usize,
    lte: usize,
    paths: Vec<String>,
}

/// 读取出来的文件的路径及行数
///
/// path: 文件路径
///
/// lines: 文件内容行数
struct StaticInfo {
    path: String,
    lines: usize,
}

/// 入口方法
///
/// 目前有两种思路，一种是返回处理结果（包含路径和文件行数）的数组
///
/// 一种是返回包含文件路径的数组，最后再根据此路径遍历读取文件行数
///
/// 此处代码是使用的引用传递，得到包含处理结果的数组
pub fn read() {
    let now = time::SystemTime::now();
    let mut options = Options {
        is_show_file: false,
        is_delete_empty_line: true,
        sort_flag: 1,
        gte: 0,
        lte: 0,
        paths: Vec::new(),
    };
    // 用来存放每个处理的文件的行数和路径
    let mut result: Vec<StaticInfo> = Vec::new();
    handle_terminal_args(&mut options);
    start_read(&options, &mut result);
    print_paths(&result, &options);
    print_static_result(&result, &options);
    print_timecost(now);
}

/// 根据命令行参数更新值
/// options: 默认配置
fn handle_terminal_args(options: &mut Options) {
    let mut list = Vec::new();
    let mut paths = Vec::new();

    // 将命令行参数转为一个Vec
    for arg in args() {
        list.push(arg);
    }

    // 从索引为1的参数开始处理
    for arg in list[1..].iter() {
        if String::from("-show-file").eq(arg) {
            options.is_show_file = true;
            continue;
        }
        if ["-h".to_string(), "-help".to_string()].contains(arg) {
            print_help_doc();
            continue;
        }
        if "-empty-line".to_string().eq(arg) {
            options.is_delete_empty_line = false;
            continue;
        }
        if arg.starts_with("-sort=") {
            let val = i8::from_str(&arg[6..]);
            if let Ok(flag) = val {
                options.sort_flag = flag;
            }
            continue;
        }
        if arg.starts_with("-gte=") {
            let val = usize::from_str(&arg[5..]);
            if let Ok(lines) = val {
                options.gte = lines;
            }
            continue;
        }
        if arg.starts_with("-lte=") {
            let val = usize::from_str(&arg[5..]);
            if let Ok(lines) = val {
                options.lte = lines;
            }
            continue;
        }
        // 如果不符合前面参数特性的，默认为都是路径
        paths.push(arg.to_string());
    }

    // 指定默认读取当前目录
    if paths.len() == 0 {
        paths.push("./".to_string());
    }
    options.paths = paths;
}

/// 循环入口路径
///
/// options: 配置
///
/// result: 统计结果
fn start_read(options: &Options, result: &mut Vec<StaticInfo>) {
    for path in options.paths.iter() {
        recursion_read(path.to_string(), options, result);
    }
}

/// 递归处理文件路径
///
/// path: 文件路径
///
/// optons: 配置
///
/// result: 统计结果
fn recursion_read(path: String, options: &Options, result: &mut Vec<StaticInfo>) {
    let stat = fs::metadata(path.clone());

    if stat.is_err() {
        return;
    }

    let file_type = stat.unwrap().file_type();

    // 过滤不支持的文件类型
    let filters = [".exe", ".pdb", ".bin", "/fonts", "/fonts/"];

    for filter in filters.iter() {
        if path.ends_with(filter) {
            return;
        }
    }

    if file_type.is_dir() {
        handle_dir(path.clone(), options, result);
    }

    if file_type.is_file() {
        let i = handle_file(path.clone(), options);
        result.push(StaticInfo { lines: i, path });
    }
}

/// 处理路径指向的是文件的情况，返回读取出来的文件的内容行数
///
/// 当未成功读取文件内容的时候，默认文件内容长度为 0
///
/// path: 文件路径
///
/// options: 配置
fn handle_file(path: String, options: &Options) -> usize {
    let file = fs::read_to_string(path.clone());

    if let Ok(f) = file {
        let lines: Vec<&str> = f.split('\n').collect();
        // 判断是否去掉空行
        if options.is_delete_empty_line {
            return lines.iter().filter(|x| !x.to_string().eq("")).count();
        }
        return lines.len();
    }
    return 0;
}

/// 处理文件路径指向的是一个目录的时候，遍历处理其下的内容
///
/// path: 文件路径
///
/// options: 配置
///
/// result: 统计结果
fn handle_dir(path: String, options: &Options, result: &mut Vec<StaticInfo>) {
    for entry in fs::read_dir(path).unwrap() {
        if entry.is_ok() {
            let file_path: fs::DirEntry = entry.unwrap();
            if let Some(path) = file_path.path().to_str() {
                recursion_read(path.to_string(), options, result);
            }
        }
    }
}

/// 打印帮助文档
fn print_help_doc() {
    println!(
        "
///***************** 帮助文档 ******************///
/// 统计文件的内容总行数
///
/// 默认读取当前目录
///
/// 用例:
///  readLines.exe [path] [-options]
///
/// path: 默认是当前read-line文件所在的路径
///
/// 参数:
///  -show-file  打印读取的文件的路径及行数
///  -h          打印帮助文档
///  -help       打印帮助文档
///  -empty-line 保留空行
///  -sort=1     文件的打印的顺序, 1 升序 -1 降序 0 读取文件时的顺序(原序) 默认是升序
///  -gte=x      文件行数大于等于 x 行的才打印
///  -lte=x      文件行数小于等于 x 行的才打印
///*****************************************///
    "
    );
}

/// 打印统计的文件路径
///
/// result: 统计结果
///
/// options: 配置
fn print_paths(result: &Vec<StaticInfo>, options: &Options) {
    if !options.is_show_file {
        return;
    }

    let mut cp_res = Vec::new();

    for res in result.iter() {
        cp_res.push(res);
    }

    if options.sort_flag > 0 {
        asc_sort(&mut cp_res);
    }

    if options.sort_flag < 0 {
        desc_sort(&mut cp_res);
    }

    filter_paths(&mut cp_res, options);

    for res in cp_res.iter() {
        println!("{} - {}", res.path, res.lines);
    }

    println!();
}

/// 打印耗时
///
/// now: 开始执行时的系统时间
fn print_timecost(now: time::SystemTime) {
    if let Ok(t) = now.elapsed() {
        println!("共用时: {}ms", t.as_millis());
    }
}

/// 根据文件行数，统计结果升序
///
/// result: 统计结果的备份
fn asc_sort(result: &mut Vec<&StaticInfo>) {
    result.sort_by_key(|k| k.lines);
}

/// 根据文件行数，统计结果降序
///
/// 先升序，然后倒序
///
/// result: 统计结果的备份
fn desc_sort(result: &mut Vec<&StaticInfo>) {
    result.sort_by_key(|k| k.lines);
    result.reverse();
}

/// 根据文件行数限制过滤统计结果
///
/// result: 统计结果的备份
///
/// options: 配置
fn filter_paths(result: &mut Vec<&StaticInfo>, options: &Options) {
    let mut i = 0;
    while i < result.len() {
        let res = result[i];
        if options.gte > 0 && options.lte > 0 {
            if res.lines < options.gte || res.lines > options.lte {
                result.remove(i);
                continue;
            }
        }
        if options.gte > 0 {
            if res.lines < options.gte {
                result.remove(i);
                continue;
            }
        }
        if options.lte > 0 {
            if res.lines > options.lte {
                result.remove(i);
                continue;
            }
        }
        i += 1;
    }
}

/// 打印统计的结果
///
/// 包括：
///
/// 打印行数最多的文件的路径
///
/// 打印统计的文件数量
///
/// result: 统计结果
///
/// options: 配置
fn print_static_result(result: &Vec<StaticInfo>, options: &Options) {
    let mut cp_res = Vec::new();

    for res in result.iter() {
        cp_res.push(res);
    }

    cp_res.sort_by_key(|k| k.lines);

    filter_paths(&mut cp_res, options);

    println!("///************* 统计结果 ***************///");
    println!();

    let last_index = cp_res.len();

    if last_index == 0 {
        return;
    }

    let max_len = cp_res.last().unwrap().lines;

    println!("------------- 行数最多的文件 -----------------\n");

    while last_index > 0 {
        if max_len > 0 && max_len.eq(&cp_res[last_index - 1].lines) {
            println!(
                "{} - {}",
                cp_res[last_index - 1].path,
                cp_res[last_index - 1].lines
            );
        }
        break;
    }

    let mut count = 0;

    for res in cp_res.iter() {
        count += res.lines;
    }

    println!();
    println!("共统计文件: {}个", last_index);
    println!("文件总行数为: {}", count);
}
